﻿// Copyright 2013 Ronald Schlenker, www.technewlogic.de.
// 
// This file is part of Byond.
// 
// Byond is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
// 
// Byond is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with Byond.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Threading;
using Autofac;
using Byond.Compilation;
using Byond.Recording;
using Byond.AudioMidi;
using Byond.EnsembleLibrary;
using Byond.Logging;
using Byond.Persistence;
using Byond.HostObjects;
using Byond.UIRuntime;
using Mvvm;
using NLog;
using NLog.Config;
using Utils;
using Utils.CtorSubstitution;
using Utils.Messaging;

namespace Byond
{
	public abstract class FrameworkBootstrapper : IDisposable
	{
		protected FrameworkBootstrapper()
		{
			_containerBuilder = new ContainerBuilder();

			LogManager.Configuration = new XmlLoggingConfiguration(
				Path.GetDirectoryName(GetType().Assembly.Location) + "\\NLog.config", false);
			Logger = LogManager.GetLogger("System");
			_containerBuilder.Register(c => Logger);

			AppDomain.CurrentDomain.UnhandledException += (s, e) =>
				{
					var messageBoxText = e.ExceptionObject == null ? "Unknown" : e.ExceptionObject.ToString();
					MessageBox.Show(messageBoxText, "Fatal Unhandled Exception Occurred", MessageBoxButton.OK);
					
					Logger.FatalException("Unhandled Exception (AppDomain)", e.ExceptionObject as Exception);
				};
			
			_containerBuilder.RegisterModule(new MessageInterceptionModule());
			_containerBuilder.RegisterModule(new CtorSubstitutionModule());

			// MVVM
			RegisterAssemblyTypes(typeof(ViewFactory).Assembly);
			// Utils
			RegisterAssemblyTypes(typeof(RelayCommand).Assembly);
			// Byond
			RegisterAssemblyTypes(typeof(FrameworkBootstrapper).Assembly);

			//SetupAutomaticAssemblyProbing();

			_containerBuilder.RegisterType<UserLogger>().As<IUserLogger>().SingleInstance();
			_containerBuilder.RegisterType<Ensemble>().FindConstructorsWith(BindingFlags.NonPublic);
			_containerBuilder.RegisterType<Library>().AsSelf().SingleInstance();
			_containerBuilder.RegisterType<Scheduler>().AsSelf().SingleInstance();
			_containerBuilder.RegisterType<EnsembleSerializer>().AsSelf().As<IEnsembleQueryService>().InstancePerDependency();
			_containerBuilder.RegisterType<PersistenceManager>().ExternallyOwned().SingleInstance();
			_containerBuilder.RegisterType<PatchManager>().FindConstructorsWith(BindingFlags.NonPublic).InstancePerLifetimeScope();
			_containerBuilder.RegisterType<UiManager>().InstancePerLifetimeScope();
			_containerBuilder.RegisterType<VoiceManager>().InstancePerLifetimeScope();
			_containerBuilder.RegisterType<TransportRegistrar>().InstancePerLifetimeScope();
			_containerBuilder.RegisterType<PatchInfo>().InstancePerLifetimeScope();
			_containerBuilder.RegisterType<KeyboardCallbackManager>().AsSelf().InstancePerLifetimeScope();
			_containerBuilder.RegisterType<MidiCallbackManager>().AsSelf().InstancePerLifetimeScope();
			_containerBuilder.RegisterType<Note96CallbackController>().InstancePerLifetimeScope();
			_containerBuilder.RegisterType<RuntimeScope>().SingleInstance();
			_containerBuilder.RegisterType<RuntimeContext>().InstancePerLifetimeScope();
			_containerBuilder.RegisterType<PatchCompilationInfo>().SingleInstance();
			_containerBuilder.RegisterGeneric(typeof(CallbackController<>)).AsSelf().SingleInstance();

			_containerBuilder.RegisterType<RuntimeInfo>().AsSelf().SingleInstance();
			_containerBuilder.RegisterType<PatchBuilder>().SingleInstance();
			_containerBuilder.RegisterType<AudioRecorder>().SingleInstance();
			_containerBuilder.RegisterType<Env>().SingleInstance();
			_containerBuilder.RegisterType<SettingsManager>().SingleInstance();
			_containerBuilder.RegisterType<UiDispatcher>().SingleInstance();
			_containerBuilder.Register(c => Container);
		}

		protected IContainer Container { get; private set; }
		
		public Logger Logger { get; private set; }
		
		private readonly ContainerBuilder _containerBuilder;
		protected ContainerBuilder ContainerBuilder { get { return _containerBuilder; } }

		//private void SetupAutomaticAssemblyProbing()
		//{
		//    AppDomain.CurrentDomain.AssemblyLoad += (s, e) =>
		//        RegisterAssemblyTypes(new[] { e.LoadedAssembly });

		//    RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies());
		//}

		//private void RegisterAssemblyTypes(Assembly[] assemblies)
		//{
		//    assemblies = assemblies
		//        .Where(it => !it.FullName.ToLower().StartsWith("system"))
		//        //.Where(it => !it.FullName.ToLower().StartsWith("microsoft"))
		//        //.Where(it => !it.FullName.ToLower().StartsWith("mscorlib"))
		//        //.Where(it => !it.FullName.ToLower().StartsWith("presentationcore"))
		//        //.Where(it => !it.FullName.ToLower().StartsWith("presentationframework"))
		//        //.Where(it => !it.FullName.ToLower().StartsWith("icsharp"))
		//        //.Where(it => !it.FullName.ToLower().StartsWith("autofac"))
		//        //.Where(it => !it.FullName.ToLower().StartsWith("naudio"))
		//        //.Where(it => !it.FullName.ToLower().StartsWith("newton"))
		//        //.Where(it => !it.FullName.ToLower().StartsWith("fasterflect"))
		//        //.Where(it => !it.FullName.ToLower().StartsWith("windows"))
		//        //.Where(it => !it.FullName.ToLower().StartsWith("nlog"))
		//        //.Where(it => !it.FullName.ToLower().StartsWith("jacobi"))
		//        .ToArray();
		//    _containerBuilder
		//        .RegisterAssemblyTypes(assemblies)
		//        .InstancePerDependency();
		//}

		protected void RegisterAssemblyTypes(Assembly assembly)
		{
			_containerBuilder
				.RegisterAssemblyTypes(assembly)
				.InstancePerDependency();
		}

		protected void Build()
		{
			Container = _containerBuilder.Build();
			ContainerBuilt();
		}

		protected virtual void ContainerBuilt()
		{
		}

		protected void InitializeRuntime()
		{
			Container.Resolve<RuntimeScope>().Reset();
		}

		protected void InitializePersistence()
		{
			// TODO: Resolve, weil die auf Messages hören müssen.
			Container.Resolve<PersistenceManager>();
		}

		protected void InitializeUi(Dispatcher dispatcher)
		{
			Container.Resolve<UiDispatcher>().Start(dispatcher);
		}

		public void Dispose()
		{
			if (Container != null)
				Container.Dispose();
		}
	}
}
